﻿namespace Universe.MediaProbe
{
    using System;
    using System.Text;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    using System.Xml;
    using System.Xml.Serialization;
    using External;

    public class ProbeReportBuilder_FF : IProbeReportBuilder
    {

        public ProbeReport BuildProbe(string fileName)
        {
            var probeBytes = BuildRawProbe(fileName);
            TryDump(fileName, probeBytes);
            ProbeReport report = ProbeReportParser.Parse(probeBytes);
            report.FullName = fileName;
            return report;
        }
        
        public byte[] BuildRawProbe(string fileName)
        {
            string exeProbe = ExternalSource.GetFFProbeFile();
            string args = "-v quiet -print_format xml -show_format -show_streams -show_error \"" + fileName + "\"";
            ProcessStartInfo si = new ProcessStartInfo(exeProbe, args)
            {
                CreateNoWindow = true,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                StandardErrorEncoding = Encoding.UTF8,
                StandardOutputEncoding = Encoding.UTF8,
                WindowStyle = ProcessWindowStyle.Hidden,
                UseShellExecute = false,
            };

            Process p = new Process()
            {
                StartInfo = si,
            };

            // error is written to output xml
            ManualResetEvent outputDone = new ManualResetEvent(false);
            string outputString = null;
            Exception outputException = null;

            Thread t2 = new Thread(() =>
            {
                try
                {
                    outputString = p.StandardOutput.ReadToEnd();
                }
                catch (Exception ex)
                {
                    outputException = ex;
                }
                finally
                {
                    outputDone.Set();
                }
            }) { IsBackground = true };

            using (p)
            {
                try
                {
                    p.Start();
                }
                catch (Exception ex)
                {
                    throw new Exception("ffprobe failed. check up ffmpeg is installed. executable is '" + exeProbe + "'", ex);
                }

                t2.Start();
                outputDone.WaitOne();
            }

            if (outputString == null)
            {
                const string message = "ffprobe any output is expected";
                if (outputException == null)
                    throw new InvalidOperationException(message);
                else
                    throw new InvalidOperationException(message, outputException);
            }

            UTF8Encoding enc = new UTF8Encoding(false);
            byte[] probeBytes = enc.GetBytes(outputString);
            TryDump(fileName, probeBytes);
            return probeBytes;
        }

        static readonly object SyncDump = new object();

        [Conditional("DEBUG")]
        static void TryDump(string fileName, byte[] probeBytes)
        {
            lock (SyncDump)
            {
                try
                {
                    File.WriteAllBytes(fileName + ".probe.xml", probeBytes);
                }
                catch
                {
                }

                try
                {
                    ProbeReport report = ProbeReportParser.Parse(probeBytes);
                    report.FullName = fileName;
                    XmlSerializer xs = new XmlSerializer(typeof (ProbeReport));
                    using (
                        FileStream fs = new FileStream(fileName + ".xml", FileMode.Create, FileAccess.Write,
                            FileShare.Read))
                    {
                        XmlTextWriter wr = new XmlTextWriter(fs, Encoding.UTF8);
                        wr.Formatting = Formatting.Indented;
                        xs.Serialize(wr, report);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Failed probe dump: " + Environment.NewLine + ex);
                }
            }
        }



    }
}
